/**
 * 
 */
package cs342.project1.mathNotebook.PKI;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

/** Represents a private key component of the RSA asymmetric encryption scheme.
 * @author Aaron Heise
 *
 */
public class SimpleRSAPrivateKey extends SimpleRSAKey implements RSAPrivateKey {
	/**
	 * 
	 */
	private static final long serialVersionUID = 9834059823509834L;
	
	protected RSAPrivateKey privateKey = null;
	
	/** Wraps an <a href="http://download.oracle.com/javase/1.4.2/docs/api/java/security/interfaces/RSAPrivateKey.html">RSAPrivateKey</a>.
	 * @param publicKey Public Key to wrap
	 */
	public SimpleRSAPrivateKey(RSAPrivateKey privateKey) {
		super();
		this.privateKey = privateKey;
	}
	
	
	/**  Creates a private key from an array of bytes
	 * @param encodedKey the encoded key
	 * @return the key object
	 * @throws SimpleRSAAlgorithmException Problem with the RSA implementation used by the JVM
	 * @throws SimpleRSAKeySpecException Problem with the encoded key
	 */
	protected static RSAPrivateKey loadPrivateKey(byte[] encodedKey) throws SimpleRSAAlgorithmException, SimpleRSAKeySpecException {
		PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encodedKey);
		try {
			return (RSAPrivateKey) getKeyFactory().generatePrivate(privKeySpec);
		} catch (InvalidKeySpecException e) {
			throw new SimpleRSAKeySpecException("Could not create key.", e);
		}
	}
	
	/** Creates a new key from a serialized representation
	 * @param encodedKey serialized representation of a public key
	 * @throws SimpleRSAKeySpecException Problem with the content of the encoded key
	 * @throws SimpleRSAAlgorithmException Problem with the RSA implementation available to the JVM
	 */
	public SimpleRSAPrivateKey(byte[] encodedKey) throws SimpleRSAAlgorithmException, SimpleRSAKeySpecException {
		this(loadPrivateKey(encodedKey));

	}
	
	/** Reads a file into a byte array
	 * @param filename Filename to read
	 * @return the bytes in the file
	 * @throws IOException Problem reading the file
	 */
	protected static byte[] readFile(String filename) throws IOException {
		FileInputStream fs = new FileInputStream(filename);
		int numBytes = fs.available();
		byte[] bytes = new byte[numBytes];
		fs.read(bytes);
		fs.close();
		return bytes;
	}
	
	/** Deserializes a private key from a file
	 * @param keyFilename name of file to read key from
	 * @throws IOException Problem with reading file
	 * @throws SimpleRSAKeySpecException  Problem with the content of the file
	 * @throws SimpleRSAAlgorithmException Problem with the RSA implementation available to the JVM
	 */
	public SimpleRSAPrivateKey(String keyFilename) throws SimpleRSAAlgorithmException, SimpleRSAKeySpecException, IOException {
		this(readFile(keyFilename));
	}
	
	/* (non-Javadoc)
	 * @see java.security.interfaces.RSAPrivateKey#getPrivateExponent()
	 */
	@Override
	public BigInteger getPrivateExponent() {
		// TODO Auto-generated method stub
		return this.privateKey.getPrivateExponent();
	}

	@Override
	protected Key getKey() {
		// TODO Auto-generated method stub
		return privateKey;
	}
	
	/** Decrypts encryptedData with this key
	 * @param encryptedData
	 * @return Returns the the bytes resulting from decrypting encryptedData with this key
	 * @throws SimpleRSAInvalidKeyException Problem with the key
	 * @throws SimpleRSADecryptionException Problem with the data
	 */
	public byte[] decrypt(byte[] encryptedData) throws SimpleRSAInvalidKeyException, SimpleRSADecryptionException {
		try {
			rsa.init(Cipher.DECRYPT_MODE, this.privateKey);
		} catch (InvalidKeyException e) {
			throw new SimpleRSAInvalidKeyException("There is a problem with the key.", e);
		}
		
		try {
			return rsa.doFinal(encryptedData);
		} catch (IllegalBlockSizeException e) {
			throw new SimpleRSADecryptionException("Error decrypting data.", e);
		} catch (BadPaddingException e) {
			throw new SimpleRSADecryptionException("Error decrypting data.", e);
		}
	}
	
	/** Serializes this key into the file with name filename
	 * @param filename name of file in which to write key
	 * @throws IOException Problem writing to file
	 */
	public void writeToFile(String filename) throws IOException {
		FileOutputStream fs = new FileOutputStream(filename, false);
		fs.write(this.getEncoded());
		fs.close();
	}

}
